<?php

namespace Swostar\Server;

use Redis;
use Swostar\Rpc\Rpc;
use Swoole\Coroutine\Http\Client;
use Swoole\Server as SwooleServer;
use Swostar\Foundation\Application;

abstract class Server
{
    protected $swooleServer;

    protected $port = 9100;

    protected $host = '0.0.0.0';

    protected $swooleConfig = [
        'task_worker_num' => 0
    ];

    protected $app;

    /**
     * 
     * @var Redis
     */
    protected $redis;
    /**
     * pid记录文件地址
     * @var string
     */
    protected $pidFile = '/runtime/swoole.pid';

    protected $pidMap = [
        'masterPid' => 0,   // 主进程id
        'managerPid' => 0,  // 管理进程id
        'workerPids' => [], // 工作进程id
        'taskPids' => []    // 任务进程id
    ];

    protected $events = [
        // 基础方法
        'server' => [
            'start'        => 'onStart',
            'shutdown'     => 'onShutdown',
            'workerStart'  => 'onWorkerStart',
            'workerStop'   => 'onWorkerStop',
            'workerExit'   => 'onWorkerExit',
            'connect'      => 'onConnect',
            'close'        => 'onClose',
            'workerError'  => 'onWorkerError',
            'managerStart' => 'onManagerStart',
            'managerStop'  => 'onManagerStop',
        ],
        // 子类方法
        'sub' => [],
        // 额外方法
        'ext' => []
    ];

    public function __construct(Application $app)
    {
        $this->app = $app;
        // 读取配置文件
        $this->initConfig();
        // 创建server
        $this->createServer();
        // 设置需要注册的回调函数
        $this->initEvents();
        // 设置swoole的回调函数
        $this->setSwooleEvents();
        // 设置swoole配置信息
        $this->swooleServer->set($this->swooleConfig);
    }

    public function start()
    {
        // 设置多端口监听
        $config = app('config');
        if ($config->get('server.http.tcpOnOff')) {
            new Rpc($this->swooleServer, $config->get('server.http.rpc'));
        }

        // 服务启动
        $this->swooleServer->start();
    }

    /**
     * 创建服务
     * @return mixed 
     */
    abstract protected function createServer();

    /**
     * 初始化事件
     * @return mixed 
     */
    abstract protected function initEvents();

    /**
     * 配置初始化
     * @return void 
     */
    protected function initConfig()
    {
        $config = app('config');

        // 获取当前对象名
        $className = explode('\\', strtolower(get_class($this)));
        $classTab = stristr(end($className), 'server', true);

        $host = 'server.' . $classTab . '.host';
        $port = 'server.' . $classTab . '.port';
        $swoole = 'server.' . $classTab . '.swoole';
        
        $this->host = !empty($config->get($host)) ? $config->get($host) : $this->host;
        $this->port = !empty($config->get($port)) ? $config->get($port) : $this->port;
        $this->swooleConfig = !empty($config->get($swoole)) ? $config->get($swoole) : $this->swooleConfig;
    }

    /**
     * 绑定swoole回调函数
     * @return void 
     */
    protected function setSwooleEvents()
    {
        foreach ($this->events as $type => $events) {
            foreach ($events as $event => $function) {
                $this->swooleServer->on($event, [$this, $function]);
            }
        }
    }

    protected function setEvent($type, $event)
    {
        if ($type == 'server') {
            return $this;
        }

        $this->events[$type] = $event;
        return $this;
    }

    /**
     * 获取配置
     * @return array 
     */
    public function getConfig(): array
    {
        return $this->swooleConfig;
    }

    /**
     * 设置配置
     * @param mixed $config 
     * @return $this 
     */
    public function setConfig($config)
    {
        $this->swooleConfig = array_map($this->swooleConfig, $config);
        return $this;
    }

    /**
     * @return Redis 
     */
    public function getRedis()
    {
        return $this->redis;
    }

    /**
     * 创建协程客户端，通过协程发送信息
     * @param string $ip ip地址
     * @param int $port 端口地址
     * @param array $data 数据
     * @param array $header header头参数
     * @return void 
     */
    public function send($ip, $port, $data, $header = [])
    {
        $cli = new Client($ip, $port);

        if ($header) {
            $cli->setHeaders($header);
        }

        if ($cli->upgrade('/')) {
            $cli->push(json_encode($data));
        }
    }





    public function onStart(SwooleServer $server)
    {
        $this->pidMap['masterPid'] = $server->master_pid;
        $this->pidMap['managerPid'] = $server->manager_pid;

        // 保存pid到文件中
        $pidStr = sprintf('%s,$s', $server->master_pid, $server->manager_pid);

        writeCache(app()->getBasePath() . $this->pidFile, $pidStr, 0);

        // 执行start事件，向路由服务器注册该server
        $event = $this->app->make('event');
        if ($event->has('start')) {
            $event->trigger('start', [$this]);
        }

    }

    public function onShutdown()
    {
    }

    public function onWorkerStart(SwooleServer $server, int $worker_id)
    {
        // 工作进程
        $this->pidMap['workerPids'] = [
            'id'  => $worker_id,
            'pid' => $server->worker_id
        ];

        $this->redis = new Redis;
        $this->redis->pconnect('127.0.0.1', 6379);
    }

    public function onWorkerStop()
    {
    }

    public function onWorkerExit()
    {
    }

    public function onConnect()
    {
    }

    public function onClose($server, $fd)
    {
    }

    public function onWorkerError()
    {
    }

    public function onManagerStart()
    {
    }

    public function onManagerStop()
    {
    }
}
